package com.open.capacity.notice.manager;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;

import javax.validation.Valid;

import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.open.capacity.common.dto.ResponseEntity;
import com.open.capacity.common.utils.EntityUtils;
import com.open.capacity.notice.context.SendMessageJobContext;
import com.open.capacity.notice.dmo.MessageRececiverDetail;
import com.open.capacity.notice.dmo.MessageRececiverGroup;
import com.open.capacity.notice.dmo.MessageTemplate;
import com.open.capacity.notice.dmo.SendMessageJob;
import com.open.capacity.notice.dmo.SendMessageTask;
import com.open.capacity.notice.dto.SendMessageJobDto;
import com.open.capacity.notice.enums.RececiverType;
import com.open.capacity.notice.enums.SendJobStatusChangeEnum;
import com.open.capacity.notice.enums.SendMessageJobStatus;
import com.open.capacity.notice.enums.SendMessageTaskStatus;
import com.open.capacity.notice.service.MessageRececiverGroupService;
import com.open.capacity.notice.service.MessageTemplateService;
import com.open.capacity.notice.service.SendMessageJobService;
import com.open.capacity.notice.service.SendMessageTaskService;
import com.open.capacity.notice.statemachine.StateMachineFactory;
import com.open.capacity.notice.utils.StringTemplateParser;
import com.open.capacity.notice.vo.MessageRececiverGroupVO;
import com.open.capacity.notice.vo.SendMessageJobVO;
import com.open.capacity.redis.repository.RedisLockService;

import cn.hutool.cron.CronUtil;
import cn.hutool.cron.pattern.CronPattern;
import io.vavr.control.Try;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@SuppressWarnings("all")
@RequiredArgsConstructor
public class SendMessageManager {

	private final RedisLockService redisLockService;

	private final TransactionTemplate transactionTemplate;

	private final StateMachineFactory stateMachineFactory;

	private final SendMessageJobService sendMessageJobService;

	private final MessageTemplateService messageTemplateService;

	private final SendMessageTaskService sendMessageTaskService;

	private final MessageRececiverGroupService messageRececiverGroupService;

	/**
	 * 创建发送任务
	 * 
	 * @param sendMessageJobDto
	 * @return
	 */
	public SendMessageJob add(SendMessageJobDto sendMessageJobDto) {
		SendMessageJob sendMessageJob = EntityUtils.toObj(sendMessageJobDto, SendMessageJob::new).create();
		SendMessageJobContext context = this.createSendMessageJobContext(sendMessageJob);
		parse(context);
		context.validate();
		sendMessageJobService.saveOrUpdate(sendMessageJob);
		delegate(sendMessageJob, context);
		return sendMessageJob;
	}

	private void parse(SendMessageJobContext context) {
		MessageTemplate template = context.getMessageTemplate();
		SendMessageJob sendMessageJob = context.getSendMessageJob();
		Map<String, String> varibles = Try.of(() -> Optional.ofNullable(sendMessageJob.getVariables()).map(info -> {
			return JSON.parseObject(info, Map.class);
		}).orElse(new HashMap<>())).getOrElse(new HashMap<>());

		// 获取短信内容
		String message = StringTemplateParser.ofMap(varibles).apply(template.getMsgContent());
		sendMessageJob.setSendMessage(message);
	}

	/**
	 * 修改发送任务，修改后不会再触发事件
	 * 
	 * @param sendMessageJobDto
	 * @return
	 */
	@Transactional
	public ResponseEntity<Boolean> edit(@Valid @RequestBody SendMessageJobDto sendMessageJobDto) {
		SendMessageJob sendMessageJob = sendMessageJobService.getById(sendMessageJobDto.getId());
		Assert.isTrue(sendMessageJobDto.getTimer() != sendMessageJob.getTimer(), "禁止修改触发类型！");
		if (!sendMessageJobDto.getTimer()) {
			try {
				CronUtil.updatePattern(sendMessageJob.getId(), new CronPattern(sendMessageJobDto.getCron()));
			} catch (Exception e) {
				log.error("修改定时任务失败", e);
			}
		}
		Assert.isTrue(sendMessageJobDto.getShield() != sendMessageJob.getShield(), "禁止修改屏蔽类型！");
		SendMessageJobContext context = this.createSendMessageJobContext(sendMessageJob);
		context.validate();
		boolean flag = sendMessageJobService
				.updateById(EntityUtils.toObj(sendMessageJobDto, SendMessageJob::new).update());

		// to_do 是否重新触发
		return ResponseEntity.succeed(flag, "编辑成功");
	}

	/**
	 * 删除发送任务
	 * 
	 * @param id
	 * @return
	 */
	@Transactional
	public ResponseEntity<Boolean> delete(@RequestParam(name = "id") String id) {
		SendMessageJob sendMessageJob = sendMessageJobService.getById(id);
		long sendingCount = sendMessageTaskService
				.count(Wrappers.<SendMessageTask>lambdaQuery().eq(SendMessageTask::getJobId, id)
						.in(SendMessageTask::getStatus, SendMessageTaskStatus.SENDING, SendMessageTaskStatus.CREATED));
		Assert.isTrue(sendingCount <= 0, "该任务存在待处理的发送请求，不允许删除");
		if (!sendMessageJob.getTimer()) {
			try {
				CronUtil.remove(id);
			} catch (Exception e) {
				log.error("删除定时任务失败", e);
			}
		}
		sendMessageTaskService.remove(Wrappers.<SendMessageTask>lambdaQuery().eq(SendMessageTask::getJobId, id));
		boolean result = sendMessageJobService.removeById(id);
		return ResponseEntity.succeed(result, "删除成功");
	}

	/**
	 * 分页
	 * 
	 * @param sendMessageJob
	 * @param page
	 * @return
	 */
	public ResponseEntity queryPageList(SendMessageJob sendMessageJob, Page page) {
		IPage<SendMessageJob> srcPageList = sendMessageJobService.page(page, Wrappers.query(sendMessageJob));
		IPage<SendMessageJobVO> pageList = srcPageList.convert(SendMessageJobVO::new);
		List<SendMessageJobVO> list = pageList.getRecords();
		if (CollectionUtils.isNotEmpty(list)) {
			Set<String> templateIds = EntityUtils.toSet(list, SendMessageJobVO::getTemplateId);
			Set<String> receciverIds = EntityUtils.toSet(list, SendMessageJobVO::getRececiver);
			List<MessageTemplate> templateList = messageTemplateService
					.list(Wrappers.<MessageTemplate>lambdaQuery().in(MessageTemplate::getId, templateIds));
			Map<String, MessageTemplate> templateMap = EntityUtils.toMap(templateList, MessageTemplate::getId);
			List<MessageRececiverGroup> receciverList = messageRececiverGroupService
					.list(Wrappers.<MessageRececiverGroup>lambdaQuery().in(MessageRececiverGroup::getId, receciverIds));
			Map<String, MessageRececiverGroup> receciverMap = EntityUtils.toMap(receciverList,
					MessageRececiverGroup::getId);
			for (Iterator<SendMessageJobVO> it = list.iterator(); it.hasNext();) {
				SendMessageJobVO vo = it.next();
				vo.setTemplateName(Try.of(() -> templateMap.get(vo.getTemplateId()).getName()).getOrElse(""));
				vo.setRececiverName(Try.of(() -> receciverMap.get(vo.getRececiver()).getName()).getOrElse(""));
			}
		}
		return ResponseEntity.succeed(pageList);
	}

	/**
	 * 明细查询
	 * 
	 * @param id
	 * @return
	 */
	public ResponseEntity<SendMessageJob> queryById(@RequestParam(name = "id") String id) {
		SendMessageJob sendMessageJob = sendMessageJobService.getById(id);
		return ResponseEntity.succeed(sendMessageJob);
	}

	/**
	 * 委托任务(立即/定时)
	 * 
	 * @param sendMessageJob
	 * @param context
	 */
	private void delegate(SendMessageJob sendMessageJob, SendMessageJobContext context) {
		if (sendMessageJob.getTimer()) {
			// 创建并分解任务单
			Assert.isTrue(sendMessageJob.isCreated(), "该发送任务状态不对，无法触发任务");
			fireManchine(sendMessageJob, context);
		} else {
			CronUtil.schedule(sendMessageJob.getId(), sendMessageJob.getCron(), () -> {
				if (redisLockService.tryLock(sendMessageJob.getId())) {
					try {
						// 二次检测
						SendMessageJob job = sendMessageJobService.getById(sendMessageJob.getId());
						if (job == null) {
							CronUtil.remove(sendMessageJob.getId());
						}
						// 二次构建联系人
						sendMessageJob.setStatus(SendMessageJobStatus.CREATED);
						fireManchine(sendMessageJob, this.createSendMessageJobContext(sendMessageJob));
					} finally {
						redisLockService.unlock(sendMessageJob.getId());
					}
				}
			});
		}
	}

	/**
	 * fire状态机变更
	 * 
	 * @param sendMessageJob
	 * @param context
	 */
	private void fireManchine(SendMessageJob sendMessageJob, SendMessageJobContext context) {
		Boolean flag = transactionTemplate.execute(transaction -> {
			try {
				context.setSendMessageJob(sendMessageJob);
				StateMachineFactory.JobStateMachine jobStateMachine = stateMachineFactory
						.getJobStateMachine(sendMessageJob.getStatus());
				jobStateMachine.fire(SendJobStatusChangeEnum.JOB_SPLIT, context);
				return Boolean.TRUE;
			} catch (Exception e) {
				transaction.setRollbackOnly();
			}
			return Boolean.FALSE;
		});
		Assert.isTrue(flag, "任务分解失败");
	}

	/**
	 * 构建充血模型对象上下文
	 * 
	 * @param sendMessageJob
	 * @return
	 */
	public SendMessageJobContext createSendMessageJobContext(SendMessageJob sendMessageJob) {
		SendMessageJobContext contex = new SendMessageJobContext();
		contex.setSendMessageJob(sendMessageJob);
		contex.setMessageTemplate(messageTemplateService.getById(sendMessageJob.getTemplateId()));
		if (sendMessageJob.isGroup()) {
			// 加载接收人组
			contex.setMessageRececiverGroupVO(messageRececiverGroupService.queryById(sendMessageJob.getRececiver()));
		} else {
			// 构建临时接收人
			MessageRececiverGroupVO messageRececiverGroupVO = new MessageRececiverGroupVO();
			MessageRececiverDetail detail = new MessageRececiverDetail();
			detail.setOwnerId(sendMessageJob.getRececiver());
			detail.setRececiver(sendMessageJob.getRececiver());
			messageRececiverGroupVO.setRecevivers(Lists.newArrayList(detail));
			contex.setMessageRececiverGroupVO(messageRececiverGroupVO);
		}
		return contex;
	}

	/**
	 * 全量定时任务
	 */
	public void crontab() {
		CompletableFuture.runAsync(() -> {
			// 缓存应用数据
			try {
				CronUtil.setMatchSecond(true);
				CronUtil.start(true);
				SendMessageJob sendMessageJob = new SendMessageJob();
				sendMessageJob.setTimer(false);
				int pageNo = 1;
				IPage<SendMessageJob> pageList;
				do {
					Page<SendMessageJob> page = new Page<>(pageNo, 10);
					pageList = sendMessageJobService.page(page, Wrappers.query(sendMessageJob));
					if (CollectionUtils.isNotEmpty(pageList.getRecords())) {
						for (Iterator<SendMessageJob> it = pageList.getRecords().iterator(); it.hasNext();) {
							sendMessageJob = it.next();
							sendMessageJob.setRececiverType(RececiverType.NULL);
							this.delegate(sendMessageJob, null);
						}
					}
					pageNo++;
				} while (pageList.getRecords().size() == 10);
			} catch (Exception e) {
				log.error("系统启动定时任务调度失败", e);
			}
		});

	}

	public ResponseEntity queryTaskPageList(SendMessageTask sendMessageTask, Page page) {
		IPage<SendMessageTask> pageList = sendMessageTaskService.page(page, Wrappers.query(sendMessageTask));
		return ResponseEntity.succeed(pageList);
	}

}
